package com.aforce3.trailertracer.optimizations;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import javax.vecmath.Point3d;

import com.aforce3.trailertracer.Ray;
import com.aforce3.trailertracer.scenes.Scene;

/** @deprecated - not yet implemented **/
public class OctTree extends BoundingBox
{
	List<BoundingBox> children = new LinkedList<BoundingBox>();
	int depth;

	public OctTree(Point3d upperLeft, Point3d lowerRight)
	{
		super(upperLeft, lowerRight);
		depth = 0;
	}
	
	public OctTree(Point3d upperLeft, Point3d lowerRight, int d)
	{
		super(upperLeft, lowerRight);
		depth = d;
	}
	
	public OctTree(BoundingBox b)
	{
		super(b.upperLeftBack, b.lowerRightFront);
		depth = 0;
	}
	
	protected boolean isParentNode()
	{
		if(children.size() == 8 && children.get(0) instanceof OctTree) return true;
		return false;
	}
	
	public void addScene(Scene scene)
	{	
		if (isParentNode()){
			//System.out.println(intersects(scene.getBoundingBox()));
			//false positives because of the spherical intersects we used
			if(!intersects(scene.getBoundingBox())) return;
			
			
			for(BoundingBox b : children){
				OctTree o = (OctTree) b;
				
				//System.out.println(o.getUpperLeftBack() + " : " + o.getLowerRightFront());
				
				if (o.intersects(scene.getBoundingBox())){
					o.addScene(scene);
					return;
				}
			}
			//should not be reached
			System.out.println("error in add scene: " + scene.getBoundingBox().getUpperLeftBack() + " : " + scene.getBoundingBox().getLowerRightFront());
			System.out.println("Scene bounds: " + getUpperLeftBack() + " : " + getLowerRightFront());
			for(BoundingBox b :children){
				System.out.println(b.getUpperLeftBack() + " : " + b.getLowerRightFront());
			}
			
			return;
		}
		
		System.out.println("depth: " + depth);
		
		
		//TODO: If there are more than ten children, divide this node into a parent OctTree node
		children.add(scene.getBoundingBox());
		
		if (children.size() > 10){
			System.out.println("SUBDIVIDING");
			subdivide();
		}
	}
	
	public void subdivide(){
		List<BoundingBox> objects = children;
		
		children = new ArrayList<BoundingBox>();
		
		//the oct trees added are going to be in this order: -x, -y, -z | -x, -y, z| -x, y, -z ...
		for (int i=0; i<8; i++){
			int a = i & 1;
			int b = (i & 2) >> 1;
			int c = (i & 4) >> 2;
			
			//System.out.println(a + ":" + b + ":" + c);
			//a is x
			double xmin, xmax, ymin, ymax, zmin, zmax;
			
			if (a == 0){
				xmin = upperLeftBack.x;
				xmax = (upperLeftBack.x + lowerRightFront.x ) / 2;
			}else{
				xmin = (upperLeftBack.x + lowerRightFront.x ) / 2;
				xmax = lowerRightFront.x;
			}
			
			if (b == 0){
				ymax = (upperLeftBack.y + lowerRightFront.y ) / 2;
				ymin = lowerRightFront.y;
			}else{
				ymax = upperLeftBack.y;
				ymin = (upperLeftBack.y + lowerRightFront.y ) / 2;
			}
			
			if (c == 0){
				zmin = (upperLeftBack.z + lowerRightFront.z ) / 2;
				zmax = lowerRightFront.z;
			}else{
				zmin = upperLeftBack.z;
				zmax = (upperLeftBack.z + lowerRightFront.z ) / 2;
			}
			
			OctTree child = new OctTree(new Point3d(xmin, ymax, zmin), new Point3d(xmax, ymin, zmax), depth + 1);
			
			//objects must be scene bounding boxes
			for(BoundingBox object : objects){
				SceneBoundingBox o = (SceneBoundingBox) object;
				if (child.intersects(object)){
					child.addScene(o.scene);
				}
			}
			
			//remove objects
			//for (BoundingBox object : child.children){
			//	objects.remove(object);
			//}
			
			children.add(child);
		}
	}

	@Override
	public Intersection getIntersection(Ray ray, HashSet<Scene> child, boolean lights)
	{
		//the speed boost of the oct tree, if the ray does not hit its bounds, return null
		if(!intersects(ray)){
			return null;
		}
		
		
		Intersection bestIntersection = null;
		double distance = -1;
		double bestDistance = -1;
		
		//TODO: Will need to change once we implement parents for an oct tree
		for(BoundingBox b : children)
		{
			Intersection newIntersection = b.getIntersection(ray, child, lights);
			if(newIntersection != null){
				distance = ray.getDistance(newIntersection.getPoint());
				if(distance > 0 && (bestDistance < 0 || distance < bestDistance)){
					bestIntersection = newIntersection;
					bestDistance = distance;
				}
			}
		}
		
		return bestIntersection;
	}
	
	//returns whether this ray intersects the oct tree
}
